package com.xb.loan.usercenter.user.user.service.impl;

import com.xb.loan.log.exception.BizException;
import com.xb.loan.usercenter.service.user.IUserIdentifyService;
import com.xb.loan.usercenter.service.user.IUserService;
import com.xb.loan.usercenter.service.user.IWinstantSessionService;
import com.xb.loan.usercenter.service.user.IWinstantUserLoginService;
import com.xb.loan.usercenter.service.user.bean.UserIdentifyVO;
import com.xb.loan.usercenter.service.user.bean.UserInfoVO;
import com.xb.loan.usercenter.service.user.bean.WinstantLoginInfoVO;
import com.xb.loan.usercenter.service.user.bean.WinstantUserSessionIdBakVO;
import com.xb.loan.usercenter.service.user.constant.UserConst;
import com.xb.loan.usercenter.service.user.exception.IUserServiceException;
import com.xb.loan.usercenter.user.util.PropertyConf;
import com.xb.loan.util.cache.redis.collection.RedisList;
import com.xb.loan.util.cache.redis.collection.Rlist;
import com.xb.loan.util.common.ObjectOperation;
import com.xb.loan.util.log.DefaultLoggerFacotry;
import com.xb.loan.util.log.LogFormatUtil;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service("winstantUserLoginService")
public class WinstantUserLoginServiceImpl implements IWinstantUserLoginService, IUserServiceException {

	private static Logger logger = DefaultLoggerFacotry.getLogger(WinstantUserLoginServiceImpl.class);
    
    private static Rlist<String> onlineUserRList = new RedisList<String>("OnlineUser", String.class, true);

    @Autowired
	IUserService userService;
    
    @Autowired
	IUserIdentifyService userIdentifyService ;
    
    @Autowired
	IWinstantSessionService winstantSessionService;
    
    @Autowired
	PropertyConf propertyConf;
    
	public WinstantLoginInfoVO createLoginInfoVO(UserInfoVO userInfoVO)throws BizException {
		
		WinstantLoginInfoVO loginInfoVO = ObjectOperation.convertByClasses(userInfoVO, WinstantLoginInfoVO.class);
		/*if(propertyConf.getExpiredTime() != null){
			loginInfoVO.setExpiredTime(propertyConf.getExpiredTime());
		}*/
    	Date curDate = new Date();
    	loginInfoVO.setLastAccessTime(curDate);
    	loginInfoVO.setLastLoginTime(curDate);
		return loginInfoVO;
	}
	
	public WinstantUserSessionIdBakVO createUserSessionIdBakVO(String userId, String sessionId)throws BizException {
		
		WinstantUserSessionIdBakVO userSessionIdBakVO = new WinstantUserSessionIdBakVO();
		userSessionIdBakVO.setUserId(userId);
		userSessionIdBakVO.setSessionId(sessionId);
		userSessionIdBakVO.setLoginFailNum(0);
		userSessionIdBakVO.setLoginFailTime(null);
		if(propertyConf.getMaxLoginFailNum() != null){
			userSessionIdBakVO.setMaxLoginFailNum(propertyConf.getMaxLoginFailNum());
		}
		if(propertyConf.getMaxLoginFailTime() != null){
			userSessionIdBakVO.setMaxLoginFailTime(propertyConf.getMaxLoginFailTime());
		}
		return userSessionIdBakVO;
	}
	
	/**
	 * 用两个Map来存储会话信息：分别是UserSessionIdBakVO和LoginInfoVO
	 * a)当用户正常登录时，两者都会存储用户登录信息，正常退出时都会清除。
	 * b)当用户非正常退出时，考虑到无法正常清除掉LoginInfoVO的Map里的用户信息， 因此增加UserSessionIdBakVO
	 */
	@Override
	public WinstantLoginInfoVO loginUser(int userType, int forceType, String loginName, String pwd) throws BizException{
		
		UserInfoVO userInfoVO = this.checkLoginType(userType, loginName);
		//登录时间次数的限制检查
		WinstantUserSessionIdBakVO userSessionIdBakVO = winstantSessionService.queryUserSessionIdBakVO(userInfoVO.getUserId());
		doCheckLoginLimit(userSessionIdBakVO);
		
		WinstantLoginInfoVO loginInfoVO = null;
		if(userSessionIdBakVO != null && userSessionIdBakVO.getSessionId() != null){
			loginInfoVO = winstantSessionService.queryUserInfoCacheOnly(userSessionIdBakVO.getSessionId());
		}
		
		if(!userInfoVO.getPwd().equals(pwd)){
			
			//登录失败后时间次数的信息缓存
			doLoginFail(userInfoVO.getUserId(),userSessionIdBakVO,loginInfoVO);
		}else{
			
			if(userSessionIdBakVO != null){
				if(userSessionIdBakVO.getSessionId() != null){
					if(loginInfoVO != null){
						long tokenEffeTime = loginInfoVO.getTokenEffective().getTime();
						//long expiredTime = loginInfoVO.getExpiredTime();
						long currentTime = System.currentTimeMillis();
						Date curDate = new Date();
						loginInfoVO.setLastAccessTime(curDate);
						loginInfoVO.setLastLoginTime(curDate);

						if(tokenEffeTime >= currentTime){
							//用户处于登录中
							if(forceType == UserConst.UserLoginForceType.NOFORCE.getCode()){
								throw logined;
							}else if(forceType == UserConst.UserLoginForceType.FORCE.getCode()){
								//强制踢出，再次登录，当session中有该用户数据时,系统认为是再次登录
								logger.info(LogFormatUtil.getFormatMsg(new String[]{"module","userId","msg"},
										new String[]{"winstantUserLoginServiceImpl.loginUser" ,userInfoVO.getUserId(),"force doAgainLogin."}));
								
								//改为同步更新
								int realFlag = UserConst.UserRealFlag.NOREAL.getCode();
								if(userInfoVO != null){
									
									if(userInfoVO.getRealId() == null || "".equals(userInfoVO.getRealId())){
										
										realFlag = UserConst.UserRealFlag.NOREAL.getCode();
										loginInfoVO.setRealFlag(UserConst.UserRealFlag.NOREAL.getCode());
										logger.info(LogFormatUtil.getFormatMsg(new String[]{"module","userId","msg"},
												new String[]{"winstantUserLoginServiceImpl.forceLogin" ,userInfoVO.getUserId(),"synUpdateOnlineUserVO NoRealCount will reduce 1!!!"}));
										
									}else{
										
										realFlag = UserConst.UserRealFlag.REAL.getCode();
										loginInfoVO.setRealFlag(UserConst.UserRealFlag.REAL.getCode());
										logger.info(LogFormatUtil.getFormatMsg(new String[]{"module","userId","msg"},
												new String[]{"winstantUserLoginServiceImpl.forceLogin" ,userInfoVO.getUserId(),"synUpdateOnlineUserVO RealCount will reduce 1!!!"}));
										
									}
									
								}
								winstantSessionService.synUpdateOnlineUserVO(realFlag, -1);
								//清除老会话
								winstantSessionService.clearUserInfoCache(userSessionIdBakVO.getSessionId());
								return doAgainLogin(userInfoVO,userSessionIdBakVO);
							}
						}else{
							//用户处于过期非登录中,此时则可以登录(相当于强制登录)
							logger.info(LogFormatUtil.getFormatMsg(new String[]{"module","userId","msg"},
									new String[]{"winstantUserLoginServiceImpl.loginUser" ,userInfoVO.getUserId(),"expired doAgainLogin."}));
							return doAgainLogin(userInfoVO,userSessionIdBakVO);
						}
					}else{
						logger.info(LogFormatUtil.getFormatMsg(new String[]{"module","userId","msg"},
								new String[]{"winstantUserLoginServiceImpl.loginUser" ,userInfoVO.getUserId(),"sessionNotExist!!!"}));
						//loginInfoVO为空，说明StatOnlineUserTask已经清理掉了超时的session信息
						return doAgainLogin(userInfoVO,userSessionIdBakVO);
					}
				}else{
					//sessionId为空说明以前是失败登录时生成的userSessionIdBakVO(主要是记录登录失败的信息)
					logger.info(LogFormatUtil.getFormatMsg(new String[]{"module","userId","msg"},
							new String[]{"winstantUserLoginServiceImpl.loginUser" ,userInfoVO.getUserId(),"sessionId null doAgainLogin."}));
					return doAgainLogin(userInfoVO,userSessionIdBakVO);
				}
			}else{
				//当为空又匹配成功时,系统认为是新登录
				logger.info(LogFormatUtil.getFormatMsg(new String[]{"module","userId","msg"},
						new String[]{"winstantUserLoginServiceImpl.loginUser" ,userInfoVO.getUserId(),"doNewLogin."}));
				return doNewLogin(userInfoVO);
			}
		}
		
		return null;
	}
	
	private WinstantLoginInfoVO doAgainLogin(UserInfoVO userInfoVO,WinstantUserSessionIdBakVO userSessionIdBakVO)throws BizException{

		//更新数据库最后一次登录时间
		userService.updateLastLoginTime(userInfoVO.getUserId(), new Date(),null,null);
		//创建返回的用户登录信息
		WinstantLoginInfoVO loginInfoVO = createLoginInfoVO(userInfoVO);
		
		int realFlag = UserConst.UserRealFlag.NOREAL.getCode();
		if(userInfoVO != null){
			
			if(userInfoVO.getRealId() == null || "".equals(userInfoVO.getRealId())){
				
				realFlag = UserConst.UserRealFlag.NOREAL.getCode();
				loginInfoVO.setRealFlag(UserConst.UserRealFlag.NOREAL.getCode());
				logger.info(LogFormatUtil.getFormatMsg(new String[]{"module","userId","msg"},
						new String[]{"winstantUserLoginServiceImpl.doAgainLogin" ,userInfoVO.getUserId(),"synUpdateOnlineUserVO NoRealCount will add 1!!!"}));
				
			}else{
				
				realFlag = UserConst.UserRealFlag.REAL.getCode();
				loginInfoVO.setRealFlag(UserConst.UserRealFlag.REAL.getCode());
				logger.info(LogFormatUtil.getFormatMsg(new String[]{"module","userId","msg"},
						new String[]{"winstantUserLoginServiceImpl.doAgainLogin" ,userInfoVO.getUserId(),"synUpdateOnlineUserVO RealCount will add 1!!!"}));
				
			}
			
		}
		//改为同步更新
		winstantSessionService.synUpdateOnlineUserVO(realFlag, 1);
		//将登录信息数据写入redis缓存
		winstantSessionService.insertUserInfoCacheOnly(loginInfoVO);
		//sessionId的记录列表
		onlineUserRList.add(loginInfoVO.getSessionId());
		//更新备份记录新的UserId和SessionId映射关系
		userSessionIdBakVO.setSessionId(loginInfoVO.getSessionId());
		userSessionIdBakVO.setLoginFailNum(0);
		userSessionIdBakVO.setLoginFailTime(null);
		winstantSessionService.updatetUserSessionIdBakVO(userSessionIdBakVO);
		return loginInfoVO;
	}
	
	private WinstantLoginInfoVO doNewLogin(UserInfoVO userInfoVO) throws BizException{
		
		//更新数据库最后一次登录时间
		userService.updateLastLoginTime(userInfoVO.getUserId(), new Date(),null,null);
		//创建返回的用户登录信息
		WinstantLoginInfoVO loginInfoVO = createLoginInfoVO(userInfoVO);
		
		int realFlag = UserConst.UserRealFlag.NOREAL.getCode();
		if(userInfoVO != null){
			
			if(userInfoVO.getRealId() == null || "".equals(userInfoVO.getRealId())){
				
				realFlag = UserConst.UserRealFlag.NOREAL.getCode();
				loginInfoVO.setRealFlag(UserConst.UserRealFlag.NOREAL.getCode());
				logger.info(LogFormatUtil.getFormatMsg(new String[]{"module","userId","msg"},
						new String[]{"winstantUserLoginServiceImpl.doNewLogin" ,userInfoVO.getUserId(),"synUpdateOnlineUserVO NoRealCount will add 1!!!"}));
			}else{
				
				realFlag = UserConst.UserRealFlag.REAL.getCode();
				loginInfoVO.setRealFlag(UserConst.UserRealFlag.REAL.getCode());
				logger.info(LogFormatUtil.getFormatMsg(new String[]{"module","userId","msg"},
						new String[]{"winstantUserLoginServiceImpl.doNewLogin" ,userInfoVO.getUserId(),"synUpdateOnlineUserVO RealCount will add 1!!!"}));
			
			}
		}
		
		//改为同步更新
		winstantSessionService.synUpdateOnlineUserVO(realFlag, 1);
		//将登录信息数据写入redis缓存
		winstantSessionService.insertUserInfoCacheOnly(loginInfoVO);
		//sessionId的记录列表
		onlineUserRList.add(loginInfoVO.getSessionId());
		//判断用户是否正常退出
		WinstantUserSessionIdBakVO userSessionIdBakVO = createUserSessionIdBakVO(loginInfoVO.getUserId(),loginInfoVO.getSessionId());
		//新增备份记录新的UserId和SessionId映射关系
		winstantSessionService.insertUserSessionIdBakVO(userSessionIdBakVO);
		
		return loginInfoVO;
	}

	public boolean loginOut(String sessionId) throws BizException {
		
		WinstantLoginInfoVO loginInfoVO = winstantSessionService.queryUserInfoCacheOnly(sessionId);
		if(loginInfoVO == null){
			//超出系统会话期，由StatOnlinUserTask自动清除了会话
			return true;
		}
		winstantSessionService.clearUserInfoCache(sessionId);
		winstantSessionService.clearUserSessionIdBakVO(loginInfoVO.getUserId());
		
		long tokenTime = loginInfoVO.getTokenEffective().getTime();
		//long expiredTime = loginInfoVO.getExpiredTime();
		long currentTime = System.currentTimeMillis();
		if(tokenTime < currentTime){
			//不在有效期的登出，不计算在线用户数，由StatOnlineUserTask去定时计算减一
			return true;
		}
		
		int realFlag = UserConst.UserRealFlag.NOREAL.getCode();
		if(loginInfoVO.getRealFlag() == UserConst.UserRealFlag.NOREAL.getCode()){
			
			realFlag = UserConst.UserRealFlag.NOREAL.getCode();
			logger.info(LogFormatUtil.getFormatMsg(new String[]{"module","userId","msg"},
					new String[]{"winstantUserLoginServiceImpl.loginOut" ,loginInfoVO.getUserId(),"synUpdateOnlineUserVO NoRealCount will reduce 1!!!"}));
		}else{
			
			realFlag = UserConst.UserRealFlag.REAL.getCode();
			logger.info(LogFormatUtil.getFormatMsg(new String[]{"module","userId","msg"},
					new String[]{"winstantUserLoginServiceImpl.loginOut" ,loginInfoVO.getUserId(),"synUpdateOnlineUserVO RealCount will reduce 1!!!"}));
		}
		//改为同步更新
		winstantSessionService.synUpdateOnlineUserVO(realFlag, -1);
		return true;
    }
    
    public int updatePwd(String userId, String pwd) throws BizException {
    	
        return userService.updatePwd(userId,pwd);
    }
    
    /**
     * 检测登录类型（1：登录名登录 2：手机号登录 3：身份证登录）
     * @param userType
     * @param loginName
     * @return
     * @throws BizException
     */
    private UserInfoVO checkLoginType(int userType,String loginName) throws BizException{
    	
    	UserInfoVO userInfoVO = null;
		if(UserConst.UserLoginType.LOGINNAME.getCode() == getLoginType(loginName)){
			
			userInfoVO = userService.queryUserInfoByUserType(userType, loginName);
		}else if(UserConst.UserLoginType.PHONE.getCode() == getLoginType(loginName)){
			
			userInfoVO = userService.queryUserInfoByPhone(userType, loginName);
		}else if(UserConst.UserLoginType.CARDNO.getCode() == getLoginType(loginName)){
			
			UserIdentifyVO uiVO = userIdentifyService.queryByCardNo(UserConst.UserIdentifyCardType.CARDNO.getCode(), loginName);
			if(uiVO != null){
				List<UserInfoVO> uiVOList = userService.queryUserInfoByRealId(UserConst.UserType.WINSTANT.getCode(),uiVO.getRealId());
				if(uiVOList != null){
					if(uiVOList.size() > 1){
						throw moreUserOneReal;
					}else if(uiVOList.size() == 1){
						userInfoVO = uiVOList.get(0);
					}
				}
			}else{
				throw userCardNoNotExist;
			}
		}else{
//			throw loginNameNotLegal;
			throw userNameOrPwdErr;
		}
		
		if(userInfoVO == null){
			throw userInfoMissing;
//			throw userNameOrPwdErr;
		}
		
		return userInfoVO;
    }
    
    /**
     * 登录之前的检查处理
     * @param userSessionIdBakVO
     * @return
     */
    private void doCheckLoginLimit(WinstantUserSessionIdBakVO userSessionIdBakVO) throws BizException {
    	
    	if(userSessionIdBakVO != null && userSessionIdBakVO.getLoginFailTime() != null){
	    	long curTime = new Date().getTime();
	    	long loginFailTime = userSessionIdBakVO.getLoginFailTime().getTime();
	    	String curDay = new SimpleDateFormat("yyyyMMdd").format(new Date());
	    	String lastLoginDay = new SimpleDateFormat("yyyyMMdd").format(userSessionIdBakVO.getLoginFailTime());
	    	if(userSessionIdBakVO.getLoginFailNum() >= userSessionIdBakVO.getMaxLoginFailNum() 
	    			&& curDay.equals(lastLoginDay) 
	    			&& (loginFailTime + userSessionIdBakVO.getMaxLoginFailTime()) > curTime){
	    		
	    			Date unfreezeTime = new Date(loginFailTime + userSessionIdBakVO.getMaxLoginFailTime());
	    			String unfreezeTimeStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(unfreezeTime);
	    			StringBuffer sb = new StringBuffer("").append(IUserServiceException.loginNumExceedPeriod.getMessage())
	    					.append("，系统将在").append(unfreezeTimeStr).append("解冻。");
//	    			throw loginNumExceedPeriod;
	    			throw new BizException("1226",sb.toString());
	    	}
    	}
    	//等于空则说明之前没有失败，无需处理。
    }
    
    /**
     * 登录失败后的逻辑处理
     * @return
     */
    private void doLoginFail(String userId, WinstantUserSessionIdBakVO userSessionIdBakVO, WinstantLoginInfoVO loginInfoVO) throws BizException {
    	
    	//用户已经登录过，还未退出的情况
    	if(userSessionIdBakVO != null && loginInfoVO != null){
			//A用户已经登录，其他人或自己用A用户名再次登录，但是密码不对
			userSessionIdBakVO.setLoginFailNum(userSessionIdBakVO.getLoginFailNum() + 1);
			userSessionIdBakVO.setLoginFailTime(new Date());
			winstantSessionService.updatetUserSessionIdBakVO(userSessionIdBakVO);
//			throw loginPasswordError;
			throw userNameOrPwdErr;
		}
    	
    	
    	if(userSessionIdBakVO != null){
    		
	    	String curDay = new SimpleDateFormat("yyyyMMdd").format(new Date());
	    	if(userSessionIdBakVO.getLoginFailTime() == null){
	    		//之前超过有效会话期，由StatOnlineUserTask定时将其loginInfoVO会话清除，随后用户登录输入密码失败
	    		updateWinstantUserSessionIdBakVO(userId);
//	    		throw loginPasswordError;
	    		throw userNameOrPwdErr;
	    	}
	    	//之前已经失败过，本次又本次登录失败
	    	String lastLoginDay = new SimpleDateFormat("yyyyMMdd").format(userSessionIdBakVO.getLoginFailTime());
	    	long curTime = new Date().getTime();
	    	long loginFailTime = userSessionIdBakVO.getLoginFailTime().getTime();
	    	if(!curDay.equals(lastLoginDay)){
	    		updateWinstantUserSessionIdBakVO(userId);
	    	}else{
	    		 if((loginFailTime + userSessionIdBakVO.getMaxLoginFailTime()) < curTime){
	    			 updateWinstantUserSessionIdBakVO(userId);
	    		 }else{
	    			 userSessionIdBakVO.setLoginFailNum(userSessionIdBakVO.getLoginFailNum() + 1);
	    			 userSessionIdBakVO.setLoginFailTime(new Date());
	    			 winstantSessionService.updatetUserSessionIdBakVO(userSessionIdBakVO);
	    		 }
	    	}
    	}else{
    		//用户之前未登录过或者之前登录过然后正常退出，本次登录失败
    		updateWinstantUserSessionIdBakVO(userId);
    	}
//    	throw loginPasswordError;
    	throw userNameOrPwdErr;
    }
    
    public void updateWinstantUserSessionIdBakVO(String userId) throws BizException {
    	
		//不是同一天或者同一天但是【最后一次登录失败时间】-【当前时间】 > 【最大登录失败时间】，则取消登录失败的限制。
		WinstantUserSessionIdBakVO userSessionIdBakVO = new WinstantUserSessionIdBakVO();
		userSessionIdBakVO.setUserId(userId);
		userSessionIdBakVO.setSessionId(null);
		userSessionIdBakVO.setLoginFailTime(new Date());
		userSessionIdBakVO.setLoginFailNum(1);
		if(propertyConf.getMaxLoginFailTime() != null){
			userSessionIdBakVO.setMaxLoginFailTime(propertyConf.getMaxLoginFailTime());
		}
		if(propertyConf.getMaxLoginFailNum() != null){
			userSessionIdBakVO.setMaxLoginFailNum(propertyConf.getMaxLoginFailNum());
		}
		//通过增加一个新的登录失败VO，取消登录失败的限制。
		winstantSessionService.updatetUserSessionIdBakVO(userSessionIdBakVO);
    }
    
    /**
     * 检测登录类型（1：登录名登录 2：手机号登录 3：身份证登录）
     * @param loginName
     * @return
     */
    private int getLoginType(String loginName){
    	
    	//检查是否是登录名登录
    	if(loginName != null){
    		
    		String regExp = "^[a-zA-Z]+$";
    		Pattern p = Pattern.compile(regExp);
    		Matcher m = p.matcher(loginName);
      	    if(m.find()){
      	    	
      	    	return UserConst.UserLoginType.LOGINNAME.getCode();
      	    }
    	}
    	
    	//检查是否是手机号登录
    	if(loginName != null && loginName.length() == 11){
    		
    		String regExp = "^[1]([3|4|5|7|8][0-9]{1})[0-9]{8}$";
    		Pattern p = Pattern.compile(regExp);
    		Matcher m = p.matcher(loginName);
      	    if(m.find()){
      	    	
      	    	return UserConst.UserLoginType.PHONE.getCode();
      	    }
    	}
    	
    	//检查是否是身份证号登录
    	if(loginName != null && loginName.length() != 11){
    		
    		String regExp = "^(\\d{14}[0-9a-zA-Z])|(\\d{17}[0-9a-zA-Z])$";
    		Pattern p = Pattern.compile(regExp);
    		Matcher m = p.matcher(loginName);
      	    if(m.find()){
      	    	
      	    	return UserConst.UserLoginType.CARDNO.getCode();
      	    }
    	}
    	
    	return 0;
    }

}
